import java.io.*;
import java.net.*;
import java.util.*;

/**
 * @author Jan Ove Kjærland (jkj092)
 * @author Martine Trulsrud (mtr086)
 * @since 2014-02-14
 */
public class SimbaServerThread extends Thread {

	protected DatagramSocket socket = null;
	protected BufferedReader in = null;

	private String NOTOK = "NOTOK";
	private String OK = "OK";
	// Navnet til den innkommende filen
	private String filename = null;
	// Navnet filen lagres som
	private String savedFileName = null;
	// Antall linjer filen skal inneholde
	private int N = -1;
	// Tegn for oppdeling av header
	private String SPLIT = "\\|";
	// Addressen til senderen
	private InetAddress address = null;

	public SimbaServerThread() throws IOException {
		this("SimbaServerThread");
	}

	public SimbaServerThread(String name) throws IOException {
		super(name);
		this.socket = new DatagramSocket(4445);

	}

	@Override
	public void run() {

		// Tar i mot pakker og legger dem i en liste.
		ArrayList<String> packets = receivePackets();

		try {
			/**
			 * TODO legge til flere parametre i responsen
			 */
			byte[] buf = new byte[256];

			if (!writeToFile(packets)) {
				buf = this.NOTOK.getBytes();
			}
			// if (backupSuccessful(this.savedFileName) ==) {
			//
			// }

			// Sender respons
			DatagramPacket packet = new DatagramPacket(buf, buf.length,
					this.address, 4445);
			this.socket.send(packet);

			// Må vite om klienten vil sende på nytt eller ikke før serveren
			// stenger socketen.
			this.socket.receive(packet);
			String answer = new String(packet.getData());
			if (answer.equals(this.OK)) {
				this.socket.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * Sjekker at overføring og lagring gikk bra ved å sjekke at antall linjer i
	 * filen stemmer overens med det antall som ble oppgitt.
	 * 
	 * @param savedFileName
	 *            navnet filen lagres under
	 * @param N
	 *            antall linjer i filen
	 * 
	 * @return OK hvis det gikk bra, NOTOK hvis noe gikk galt. Dette sammen med
	 *         et utvalg passende parametre. Eksempel: OK|FILNAVN eller
	 *         NOTOK|L|T
	 */
	public String backupSuccessful(String savedFileName) {
		try (BufferedReader reader = new BufferedReader(new FileReader(
				savedFileName))) {

			int counter = 0;
			while (reader.readLine() != null) {
				counter++;
			}

			if (counter == this.N) {
				return this.OK;
			}
			return this.NOTOK;

		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return this.NOTOK;
		} catch (IOException e) {
			e.printStackTrace();
			return this.NOTOK;
		}

	}

	/**
	 * Mottar datagrammer fra en sender og behandler disse basert på kommandoer
	 * gitt i headeren. Per nå er "PUT" den eneste godkjente kommandoen. Metoden
	 * lagrer et utvalg variabler som er relevante for overføringen;
	 * senderadresse, antall pakker (linjer i filen), og filnavn.
	 * 
	 * @return en liste med strenger
	 */
	public ArrayList<String> receivePackets() {

		// En liste med alle innkommende pakker.
		ArrayList<String> packets = new ArrayList<String>();
		// Hvorvidt filoverføringen er fullført
		boolean finished = false;
		DatagramPacket packet = null;

		byte[] buf = null;

		while (!finished) {
			try {
				buf = new byte[256];

				// Mottar pakker
				packet = new DatagramPacket(buf, buf.length);
				this.socket.receive(packet);

				String line = new String(packet.getData());
				// Sjekker at headeren starter med rett kommando.
				if (!line.startsWith("PUT")) {
					continue;
				}
				packets.add(line);

				String[] fields = line.split(this.SPLIT);

				// Hvis filnavn, antall linjer og senders adresse
				// ikke allerede er lagret, blir det gjort her.
				if (this.filename == null && this.N == -1
						&& this.address == null) {
					this.filename = fields[1];
					this.N = Integer.parseInt(fields[2]);
					this.address = packet.getAddress();
				}
				if (packets.size() == Integer.parseInt(fields[2])) {
					finished = true;
				}
			} catch (IOException e) {
				e.printStackTrace();
				finished = true;
			}
		}
		return packets;
	}

	/**
	 * Skriver de mottatte linjene til en lokal fil.
	 * 
	 * @param packets
	 *            en liste med pakker som skal skrives til fil
	 * @return true hvis skriving til fil gikk som det skulle.
	 */
	public boolean writeToFile(ArrayList<String> packets) {

		String[] lines = new String[packets.size()];
		// Sorterer linjene i en ny tabell i tilfelle de ikke ble mottatt i rett
		// rekkefølge
		for (int i = 0; i < packets.size(); i++) {
			String[] fields = packets.get(i).split(this.SPLIT);
			lines[Integer.parseInt(fields[3]) - 1] = fields[4];
		}

		// For å unngå overskriving av filen under testing, legger vi til
		// "backup_" til filnavnet som prefix.
		this.savedFileName = "backup_" + this.filename;

		try (PrintWriter printer = new PrintWriter(this.savedFileName, "UTF-8")) {
			// Skriver linjene til den nye filen.
			for (String s : lines) {
				printer.println(s.trim());
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
}
